/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2021 Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 * Copyright (c) 2021 Abrar (https://gitlab.com/s96Abrar)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

#pragma once

#include <QObject>
#include <QImage>
#include <QRect>
#include <QMap>

struct wl_seat;
struct wl_array;
struct wl_output;
struct wl_surface;
struct zwayfire_toplevel_manager_v1;
struct zwayfire_toplevel_manager_v1_listener;
struct zwayfire_toplevel_v1;
struct zwayfire_toplevel_v1_listener;

namespace WQt {
    class ToplevelManager;
    class Toplevel;
    typedef QList<Toplevel *> Toplevels;
}

class WQt::ToplevelManager : public QObject {
    Q_OBJECT;

    public:
        ToplevelManager( zwayfire_toplevel_manager_v1 *winMgr );
        ~ToplevelManager();

        void setup();

        /** Get the underlying object */
        zwayfire_toplevel_manager_v1 *get();

        /** Get the zwayfire_toplevel_v1 from uuid */
        void getToplevelForUuid( uint32_t uuid );

        /** Request the compositor to send all the toplevels */
        void sendAllToplevels();

        /** Get a list of already registered toplevels */
        WQt::Toplevels toplevels();

    private:

        /**
         * Get the handle, and send out a signal.
         */
        static void handleNewToplevel( void *, zwayfire_toplevel_manager_v1 *, zwayfire_toplevel_v1 *, uint32_t );

        /**
         * Get the handle, and send out a signal.
         */
        static void handleToplevel( void *, zwayfire_toplevel_manager_v1 *, zwayfire_toplevel_v1 *, uint32_t );

        /**
         * Compositor is done with this object - delete it.
         */
        static void handleFinished( void *, zwayfire_toplevel_manager_v1 * );

        /**
         * Compositor is done sending a group of events - specifically toplevels.
         */
        static void handleDone( void *, zwayfire_toplevel_manager_v1 * );

        /** Listener */
        static const zwayfire_toplevel_manager_v1_listener mTlMgrListener;

        /** The underlying wayland object */
        zwayfire_toplevel_manager_v1 *mObj;

        /** Registered toplevels */
        QMap<uint32_t, WQt::Toplevel *> mToplevels;
        QMap<uint32_t, WQt::Toplevel *> mPendingToplevels;

        /** Flag to ensure setup() is called only once. */
        bool mIsSetup = false;

        /**
         * Collect all the handles received as new toplevels and emit receivedToplevels
         * Once done,
         */
        bool mFirstTime = true;

    Q_SIGNALS:

        /**
         * Get a new handle: Run setup()
         */
        void newToplevel( WQt::Toplevel *handle );

        /**
         * Get the existing handle. Already setup.
         */
        void toplevel( WQt::Toplevel *handle );

        /**
         * Compositor is done with this
         */
        void finished();

        /**
         * Toplevels received signal
         */
        void receivedToplevels( WQt::Toplevels );
};

class WQt::Toplevel : public QObject {
    Q_OBJECT;

    public:
        enum State {
            Active           = 1 << 0,
            Minimized        = 1 << 1,
            Maximized        = 1 << 2,
            Fullscreen       = 1 << 3,
            PinnedAbove      = 1 << 4,
            PinnedBelow      = 1 << 5,
            Sticky           = 1 << 6,
            DemandsAttention = 1 << 7,
        };

        Q_DECLARE_FLAGS( States, State );

        Toplevel( zwayfire_toplevel_v1 *handle, uint32_t id );
        ~Toplevel();

        void setup();

        zwayfire_toplevel_v1 *get();

        uint32_t uuid();
        QString title();
        QString appId();
        States states();
        uint32_t parent();

        /**
         * These are convenience functions
         * These can be used as an alternative to state().testFlag(...)
         */
        bool isActive();
        bool isMinimized();
        bool isMaximized();
        bool isFullscreen();
        bool isPinnedAbove();
        bool isPinnedBelow();
        bool isSticky();
        bool isDemandingAttention();

        void focus();
        void maximize();
        void minimize();
        void restore();
        void pinAbove( bool above );
        void pinBelow( bool below );
        void setSticky( bool sticky );
        void capture();
        void startPreview();
        void stopPreview();
        void setMinimizedGeometry( QWindow *, QRect );
        void getProcessInfo();
        void close();
        void kill();

    private:
        static void handleTitleChanged( void *, struct zwayfire_toplevel_v1 *, const char *, const char * );
        static void handleAppIdChanged( void *, struct zwayfire_toplevel_v1 *, const char *, const char * );
        static void handleStateChanged( void *, struct zwayfire_toplevel_v1 *, uint32_t, uint32_t );
        static void handleClosed( void *, struct zwayfire_toplevel_v1 * );
        static void handleParentChanged( void *, struct zwayfire_toplevel_v1 *, uint32_t, uint32_t );
        static void handleGeometryChanged( void *, struct zwayfire_toplevel_v1 *, int, int, int, int );
        static void handleViewCaptured( void *, struct zwayfire_toplevel_v1 *, uint32_t, uint32_t, uint32_t );
        static void handleFrameReady( void *, struct zwayfire_toplevel_v1 *, uint32_t, uint32_t, uint32_t );
        static void handleProcessInfo( void *, struct zwayfire_toplevel_v1 *, uint32_t, uint32_t, uint32_t );
        static void handleDone( void *, struct zwayfire_toplevel_v1 * );

        static const zwayfire_toplevel_v1_listener mToplevelListener;

        zwayfire_toplevel_v1 *mObj;

        /** Flag to ensure setup() is called only once. */
        bool mIsSetup = false;

        uint32_t mUUID = 0;
        QString mTitle;
        QString mAppId;
        uint32_t mStates;
        uint32_t mParent;

        QStringList pendingTitleChange;
        QStringList pendingAppIDChange;
        QList<uint32_t> pendingStateChange;

    Q_SIGNALS:
        void titleChanged( QString, QString );
        void appIdChanged( QString, QString );
        void stateChanged( uint32_t, uint32_t );
        void closed();
        void parentChanged( uint32_t, uint32_t );
        void geometryChanged( QRect );
        void viewCaptured( QImage );
        void frameReady( QImage );
        void processInfo( uint32_t, uint32_t, uint32_t );
        void done();
};
